package com.torfox.services;

import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.stereotype.Service;

import com.torfox.entities.LogRecord;

/**
 * Implementation of {@link ILoggingService}
 * @author Bartosz Jankiewicz
 */
@Slf4j
@Service
public class LoggingService implements ILoggingService
{
	@Autowired
	@Setter
	private JpaTemplate jpaTemplate;

	@Override
	@SuppressWarnings(
	{
	        "rawtypes", "unchecked"
	})
	public List<LogRecord> getLogs(final int limit)
	{
		try
		{
			final List<LogRecord> result = jpaTemplate.execute(new JpaCallback<List<LogRecord>>()
			{
				@Override
				public List<LogRecord> doInJpa(final EntityManager em) throws PersistenceException
				{
					final Query query = em.createNamedQuery("RECENT_LOGS");
					query.setMaxResults(limit);
					final List list = query.getResultList();
					return list;
				}
			});
			return result;
		}
		catch (final Exception e)
		{
			log.error("Collecting records failed", e);
			return Collections.emptyList();
		}
	}

	@Override
	public List<LogRecord> getLogs(final int limit, final Date dateStart, final Date dateEnd,
			final String sender,
			final String filterLevel,
	        final String messagePattern,
	        final String classPattern,
	        final String exceptionPattern)
	{
		try
		{
			final List<LogRecord> result = jpaTemplate.execute(new JpaCallback<List<LogRecord>>()
			{
				@SuppressWarnings(
				{
				        "unchecked", "rawtypes"
				})
				@Override
				public List<LogRecord> doInJpa(final EntityManager em) throws PersistenceException
				{
					final CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
					final CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
					final Root from = criteriaQuery.from(LogRecord.class);

					final LinkedList<Predicate> predicates = new LinkedList<Predicate>();
					if (dateStart != null)
					{
						predicates.add(criteriaBuilder.ge(from.get("timestamp"), dateStart.getTime()));
					}
					if (dateEnd != null)
					{
						predicates.add(criteriaBuilder.le(from.get("timestamp"), dateEnd.getTime()));
					}
					if (!StringUtils.isBlank(sender))
					{
						predicates.add(criteriaBuilder.like(from.get("sender"), sender));
					}
					if (!StringUtils.isBlank(filterLevel))
					{
						predicates.add(criteriaBuilder.like(from.get("level"), filterLevel));
					}
					if (!StringUtils.isBlank(messagePattern))
					{
						predicates.add(criteriaBuilder.like(from.get("message"), "%" + messagePattern + "%"));
					}
					if (!StringUtils.isBlank(classPattern))
					{
						predicates.add(criteriaBuilder.like(from.get("className"), "%" + classPattern + "%"));
					}
					if (!StringUtils.isBlank(exceptionPattern))
					{
						predicates.add(criteriaBuilder.like(from.get("exceptionClass"), "%" + exceptionPattern + "%"));
					}
					final Predicate and = criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
					criteriaQuery.where(and);
					final CriteriaQuery select = criteriaQuery.select(from);
					select.orderBy( criteriaBuilder.desc(from.get("timestamp")));
					final TypedQuery query = em.createQuery(select);
					query.setMaxResults(limit);
					return query.getResultList();
				}
			});
			return result;
		}
		catch (final Exception e)
		{
			log.error("Collecting records failed", e);
			return Collections.emptyList();
		}
	}

	@Override
	public List<LogRecord> getLogs(final int limit, final String filterLevel, final String pattern)
	{
		try
		{
			final List<LogRecord> result = jpaTemplate.execute(new JpaCallback<List<LogRecord>>()
			{
				@SuppressWarnings({
                        "rawtypes", "unchecked"
                })
                @Override
				public List<LogRecord> doInJpa(final EntityManager em) throws PersistenceException
				{
					final Query query = em.createNamedQuery("RECENT_LOGS_BY_LEVEL_AND_PATTERN");
					query.setMaxResults(limit);
					query.setParameter("level", filterLevel);
					query.setParameter("pattern", "%" + pattern + "%");
					final List list = query.getResultList();
					return list;
				}
			});
			return result;
		}
		catch (final Exception e)
		{
			log.error("Collecting records failed", e);
			return Collections.emptyList();
		}
	}

	@Override
	public LogRecord getLogRecord(final Long recordId)
	{
		final LogRecord record = jpaTemplate.find(LogRecord.class, recordId);
		return record;
	}

	@Override
	public List<String> getLevels()
	{
		return Arrays.asList(
		        Level.DEBUG.toString(), Level.INFO.toString(), Level.WARN.toString(), Level.ERROR.toString(),
		        Level.FATAL.toString());
	}

	@Override
	public List<String> getExceptions(final String pattern)
	{
		try
		{
			final List<String> result = jpaTemplate.execute(new JpaCallback<List<String>>()
			{
				@SuppressWarnings({
                        "rawtypes", "unchecked"
                })
                @Override
				public List<String> doInJpa(final EntityManager em) throws PersistenceException
				{
					final Query query = em.createNamedQuery("UNIQUE_EXCEPTIONS");
					query.setParameter("exceptionPattern", "%" + pattern + "%");
					final List list = query.getResultList();
					return list;
				}
			});
			return result;
		}
		catch (final Exception e)
		{
			log.error("Collecting exception types failed", e);
			return Collections.emptyList();
		}
	}

	@Override
    public java.util.List<String> getSenders()
	{
		try
		{
			final List<String> result = jpaTemplate.execute(new JpaCallback<List<String>>()
			{
				@SuppressWarnings({
                        "unchecked", "rawtypes"
                })
                @Override
				public List<String> doInJpa(final EntityManager em) throws PersistenceException
				{
					final Query query = em.createNamedQuery("UNIQUE_SENDERS");
                    final List list = query.getResultList();
					return list;
				}
			});
			return result;
		}
		catch (final Exception e)
		{
			log.error("Collecting senders failed", e);
			return Collections.emptyList();
		}
	};
}
